package com.ifonly.mvc;

import com.google.common.base.Strings;
import com.ifonly.activerecord.Db;
import com.ifonly.activerecord.Model;
import com.ifonly.activerecord.Record;
import com.ifonly.activerecord.Table;
import com.ifonly.common.ApiMessage;
import com.ifonly.common.typeconverter.TypeConvert;
import com.ifonly.datatables.DTResponse;
import com.ifonly.logger.Logger;
import com.ifonly.render.Render;
import com.ifonly.render.RenderFactory;
import com.ifonly.render.renders.RedirectRender;
import com.ifonly.upload.IFile;
import com.ifonly.upload.MultipartServletRequest;
import com.ifonly.utils.BeanUtils;
import com.ifonly.utils.ReflectUtils;
import com.ifonly.utils.StringUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Field;
import java.util.Enumeration;
import java.util.Map;

/**
 * @author <a href="mailto:ifonlymaster@163.com">ifonly</a>
 * @version 1.0 2015-12-09 14:51
 * @since JDK 1.6
 */
public class Controller {
    private HttpServletRequest request;
    private HttpServletResponse response;
    private String[] params; //URL 路径解析出来的参数

    protected Logger logger;

    public void init(HttpServletRequest request, HttpServletResponse response, String[] params) {
        this.request = request;
        this.response = response;
        this.params = params;

        this.logger = Logger.getLogger(Controller.class);
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public HttpServletResponse getResponse() {
        return response;
    }


    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Attribute
    public void setAttr(String name, Object value) {
        request.setAttribute(name, value);
    }

    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Session Attribute
    public void setSessionAttr(String name, Object value) {
        HttpSession session = request.getSession();
        session.setAttribute(name, value);
    }

    public Object getSessionAttr(String name) {
        HttpSession session = request.getSession();
        return session.getAttribute(name);
    }

    // ------------------------------------------ Parameter

    public String getParam(String name) {
        return request.getParameter(name);
    }

    public String getParam(String name, String defaultValue) {
        String result = request.getParameter(name);
        return result == null ? defaultValue : result;
    }

    public String getParam(int index) {
        if (params != null && params.length > 0) {
            if (index >= params.length) {
                return null;
            }
            return params[index];
        }
        return null;
    }

    public String[] getParams(String name) {
        return request.getParameterValues(name);
    }

    public int getInt(int index) {
        return getInt(index, 0);
    }

    public int getInt(int index, int defaultValue) {
        Integer result = getInteger(index);
        if (result == null) {
            return defaultValue;
        }
        return result;
    }

    public Integer getInteger(int index) {
        Object object = getParam(index);
        return TypeConvert.convert(Integer.class, object);
    }

    public long getLong(int index) {
        return getLong(index, 0);
    }

    public long getLong(String name) {
        String value = getParam(name);
        return TypeConvert.convert(Long.class, value);
    }

    public long getLong(int index, long defaultValue) {
        Long result = getLongWrap(index);
        if (result == null) {
            return defaultValue;
        }
        return result;
    }

    public Long getLongWrap(int index) {
        Object object = getParam(index);
        return TypeConvert.convert(Long.class, object);
    }

    public Map<String, IFile[]> getFileMap() {
        MultipartServletRequest multipartServletRequest = (MultipartServletRequest) request;
        return multipartServletRequest.getFileMap();
    }

    public <T> T getModel(Class<T> clazz) {
        String classSimpleName = clazz.getSimpleName();
        String modelName = StringUtils.firstCharToLowerCase(classSimpleName);
        return getModel(clazz, modelName);
    }


    @SuppressWarnings("unchecked")
    public <T> T getModel(Class<T> clazz, String model) {
        Enumeration<String> names = request.getParameterNames();
        if (names == null || Strings.isNullOrEmpty(model)) {
            return null;
        }
        String prefix = model + ".";
        int len = prefix.length();
        if (Model.class.isAssignableFrom(clazz)) { //Model 的子类
            Model obj = null;
            while (names.hasMoreElements()) {
                try {

                    String n = names.nextElement();
                    if (n.startsWith(prefix)) {
                        String[] value = request.getParameterValues(n);
                        if (obj == null) {
                            obj = (Model) ReflectUtils.newInstance(clazz);
                        }

                        String name = n.substring(len);
                        Class<?> columnType = obj.getColumnType(name);
                        if (columnType != null) {
                            if (!columnType.isArray() && value != null && value.length > 0) {
                                if (!Strings.isNullOrEmpty(value[0])) {
                                    try {
                                        obj.set(name, TypeConvert.convert(columnType, value[0]));
                                    } catch (Exception e) {
                                        try {
                                            obj.set(name, value[0]);
                                        } catch (Exception e2) {
                                            // ignore
                                        }
                                    }
                                }
                            } else {
                                try {
                                    obj.set(name, TypeConvert.convert(columnType, value));
                                } catch (Exception e) {
                                    try {
                                        obj.set(name, value);
                                    } catch (Exception e2) {
                                        // ignore
                                    }
                                }
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }

            return (T) obj;
        } else {
            T obj = null;
            while (names.hasMoreElements()) {

                try {
                    String n = names.nextElement();
                    if (n.startsWith(prefix)) {
                        String[] value = request.getParameterValues(n);
                        if (obj == null) {
                            obj = ReflectUtils.newInstance(clazz);
                        }

                        String name = n.substring(len);

                        Field field = ReflectUtils.getDeclaredField(clazz, name);
                        try {
                            BeanUtils.setProperty(obj, field, value);
                        } catch (Exception e) {
                            // ignore
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }


            }
            return obj;
        }

    }

    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ redirect
    public void redirect(String view) {
        render = new RedirectRender(view);
    }

    // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ render
    private Render render;

    public Render getRender() {
        return render;
    }

    public void renderDataTables(Class<? extends Model> clazz) {
        Table table = Db.getInstance().getTable(clazz);
        if (table == null) {
            throw new RuntimeException("Model Class ["+ clazz.getName() +"] not mapping");
        }
        String tableName = table.getTableName();
        renderDataTables(tableName);
    }

    public void renderDataTables(String tableName) {
        DTResponse<Record> dtResponse = DTResponse.newInstance(request, "FROM " + tableName, "WHERE");
        renderJson(dtResponse);
    }

    public void renderDataTables(String segment, String keyword) {
        DTResponse<Record> dtResponse = DTResponse.newInstance(request, segment, keyword);
        renderJson(dtResponse);
    }

    public void renderJson(Object object) {
        render = RenderFactory.getJsonRender(object);
    }

    public void renderSuccess() {
        renderJson(ApiMessage.success());
    }

    public <E> void renderSuccess(E data) {
        renderJson(ApiMessage.success(data));
    }

    public void renderFailed() {
        renderJson(ApiMessage.failed());
    }

    public void renderFailed(String message) {
        renderJson(ApiMessage.failed(message));
    }

    public void renderText(String text) {
        render = RenderFactory.getTextRender(text);
    }

    public void renderJsp(String view) {
        render = RenderFactory.getJspRender(view);
    }

    public void renderFtl(String view) {
        render = RenderFactory.getFtlRender(view);
    }

    public void render(String view) {
        render = RenderFactory.getRender(view);
    }
}
